package handler

import (
	"context"
	"fmt"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm/clause"
	"mxshop_srvs/inventory_srv/global"
	"mxshop_srvs/inventory_srv/model"
	"mxshop_srvs/inventory_srv/proto"
	"sync"
)

type Inventory struct {
	proto.UnimplementedInventoryServer
}

func (i *Inventory) SetInv(ctx context.Context, req *proto.GoodsInvInfo) (*emptypb.Empty, error) {

	var inventory model.Inventory
	result := global.DB.Where(model.Inventory{Goods: req.GoodsId}).Find(&inventory)

	if result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	inventory.Goods = req.GoodsId
	inventory.Stocks = req.Num
	if result := global.DB.Save(&inventory); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	return &emptypb.Empty{}, nil
}

func (i *Inventory) InvDetail(ctx context.Context, req *proto.GoodsInvInfo) (*proto.GoodsInvInfo, error) {

	var inventory model.Inventory
	if result := global.DB.Where(model.Inventory{Goods: req.GoodsId}).Find(&inventory); result.Error != nil {
		return nil, status.Error(codes.Internal, result.Error.Error())
	}
	return &proto.GoodsInvInfo{
		GoodsId: inventory.Goods,
		Num:     inventory.Stocks,
	}, nil

}

// go 互斥锁版本

var lock sync.Mutex

func (i *Inventory) Sell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	//用到事务
	//查询库存是否还有
	tx := global.DB.Begin()
	lock.Lock()
	for _, goods := range req.GoodsInfo {
		var inventory model.Inventory
		if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		if inventory.Stocks < goods.Num {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		inventory.Stocks -= goods.Num //微服务的话还需要分布式锁来保证
		if result := tx.Save(&inventory); result.Error != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, result.Error.Error())
		}
	}
	tx.Commit()
	lock.Unlock()
	return &emptypb.Empty{}, nil
}

// mysql互斥锁版本
func (i *Inventory) Sell1(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	//用到事务
	//查询库存是否还有
	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		var inventory model.Inventory
		if result := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		if inventory.Stocks < goods.Num {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		inventory.Stocks -= goods.Num //微服务的话还需要分布式锁来保证
		if result := tx.Save(&inventory); result.Error != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, result.Error.Error())
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

// mysql 乐观锁版本
func (i *Inventory) Sell2(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	//用到事务
	//查询库存是否还有
	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		for {
			var inventory model.Inventory
			if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
				tx.Rollback()
				return nil, status.Error(codes.NotFound, "库存不足")
			}
			if inventory.Stocks < goods.Num {
				tx.Rollback()
				return nil, status.Error(codes.NotFound, "库存不足")
			}
			inventory.Stocks -= goods.Num //微服务的话还需要分布式锁来保证

			if result := tx.Model(&model.Inventory{}).Select("stocks", "version").
				Where("goods = ? and version = ?", goods.GoodsId, inventory.Version).
				Updates(model.Inventory{Stocks: inventory.Stocks, Version: inventory.Version + 1}); result.RowsAffected == 0 {
				zap.S().Info("库存扣减失败")
			} else {
				break
			}
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

// redis分布式锁
func (i *Inventory) Sell3(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	//用到事务
	//查询库存是否还有
	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		mutex := global.ReDSync.NewMutex(fmt.Sprintf("goodID-%d", goods.GoodsId))
		if err := mutex.Lock(); err != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务加锁失败")
		}
		var inventory model.Inventory
		if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		if inventory.Stocks < goods.Num {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		inventory.Stocks -= goods.Num //微服务的话还需要分布式锁来保证

		if result := tx.Model(&model.Inventory{}).Select("stocks", "version").
			Where("goods = ? and version = ?", goods.GoodsId, inventory.Version).
			Updates(model.Inventory{Stocks: inventory.Stocks, Version: inventory.Version + 1}); result.RowsAffected == 0 {
			zap.S().Info("库存扣减失败")
		}
		if ok, err := mutex.Unlock(); err != nil || !ok {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务解锁失败")
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

func (i *Inventory) Reback(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		var inventory model.Inventory
		if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		inventory.Stocks += goods.Num
		if result := tx.Save(&inventory); result.Error != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, result.Error.Error())
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

func (Inventory) TrySell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		mutex := global.ReDSync.NewMutex(fmt.Sprintf("goodID-%d", goods.GoodsId))
		if err := mutex.Lock(); err != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务加锁失败")
		}
		var inventory model.InventoryNew
		if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		if inventory.Stocks < goods.Num {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		//inventory.Stocks -= goods.Num //微服务的话还需要分布式锁来保证
		inventory.Freeze += goods.Num
		//if result := tx.Model(&model.Inventory{}).Select("stocks", "version").
		//	Where("goods = ? and version = ?", goods.GoodsId, inventory.Version).
		//	Updates(model.Inventory{Stocks: inventory.Stocks, Version: inventory.Version + 1}); result.RowsAffected == 0 {
		//	zap.S().Info("库存扣减失败")
		//}
		tx.Save(&inventory)
		if ok, err := mutex.Unlock(); err != nil || !ok {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务解锁失败")
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

func (Inventory) ConfirmSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		mutex := global.ReDSync.NewMutex(fmt.Sprintf("goodID-%d", goods.GoodsId))
		if err := mutex.Lock(); err != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务加锁失败")
		}
		var inventory model.InventoryNew
		if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		if inventory.Stocks < goods.Num {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		inventory.Stocks -= goods.Num
		inventory.Freeze -= goods.Num
		tx.Save(&inventory)
		if ok, err := mutex.Unlock(); err != nil || !ok {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务解锁失败")
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

func (Inventory) CancelSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {

	tx := global.DB.Begin()
	for _, goods := range req.GoodsInfo {
		mutex := global.ReDSync.NewMutex(fmt.Sprintf("goodID-%d", goods.GoodsId))
		if err := mutex.Lock(); err != nil {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务加锁失败")
		}
		var inventory model.InventoryNew
		if result := tx.Where(model.Inventory{Goods: goods.GoodsId}).Find(&inventory); result.RowsAffected == 0 {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		if inventory.Stocks < goods.Num {
			tx.Rollback()
			return nil, status.Error(codes.NotFound, "库存不足")
		}
		inventory.Freeze -= goods.Num
		tx.Save(&inventory)
		if ok, err := mutex.Unlock(); err != nil || !ok {
			tx.Rollback()
			return nil, status.Error(codes.Internal, "库存服务解锁失败")
		}
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}
